/* -------------------------------------------------------------------------
 *
 * pgstat_xact.c
 *	  针对累积统计系统的事务集成。
 *
 * Copyright (c) 2001-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/activity/pgstat_xact.c
 * -------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/transam.h"
#include "access/xact.h"
#include "pgstat.h"
#include "utils/memutils.h"
#include "utils/pgstat_internal.h"


typedef struct PgStat_PendingDroppedStatsItem
{
	xl_xact_stats_item item;
	bool		is_create;
	dlist_node	node;
} PgStat_PendingDroppedStatsItem;


static void fc_AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *fc_xact_state, bool fc_isCommit);
static void fc_AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *fc_xact_state,
											bool fc_isCommit, int fc_nestDepth);

static PgStat_SubXactStatus *pgStatXactStack = NULL;


/*
 * 从 access/transam/xact.c 调用，在顶层事务提交/回滚时。
 */
void AtEOXact_PgStat(bool fc_isCommit, bool fc_parallel)
{
	PgStat_SubXactStatus *fc_xact_state;

	AtEOXact_PgStat_Database(fc_isCommit, fc_parallel);

	/* 处理事务统计信息 */
	fc_xact_state = pgStatXactStack;
	if (fc_xact_state != NULL)
	{
		Assert(fc_xact_state->nest_level == 1);
		Assert(fc_xact_state->prev == NULL);

		AtEOXact_PgStat_Relations(fc_xact_state, fc_isCommit);
		fc_AtEOXact_PgStat_DroppedStats(fc_xact_state, fc_isCommit);
	}
	pgStatXactStack = NULL;

	/* 确保任何统计快照被丢弃 */
	pgstat_clear_snapshot();
}

/*
 * 在提交时，丢弃在事务中已丢弃对象的统计信息。在回滚时，丢弃在事务中创建对象的统计信息。
 */
static void fc_AtEOXact_PgStat_DroppedStats(PgStat_SubXactStatus *fc_xact_state, bool fc_isCommit)
{
	dlist_mutable_iter fc_iter;
	int			fc_not_freed_count = 0;

	if (fc_xact_state->pending_drops_count == 0)
	{
		Assert(dlist_is_empty(&fc_xact_state->pending_drops));
		return;
	}

	dlist_foreach_modify(fc_iter, &fc_xact_state->pending_drops)
	{
		PgStat_PendingDroppedStatsItem *fc_pending =
		dlist_container(PgStat_PendingDroppedStatsItem, node, fc_iter.cur);
		xl_xact_stats_item *fc_it = &fc_pending->item;

		if (fc_isCommit && !fc_pending->is_create)
		{
			/*
			 * 丢弃对象的事务已提交。也丢弃统计信息。
			 */
			if (!pgstat_drop_entry(fc_it->kind, fc_it->dboid, fc_it->objoid))
				fc_not_freed_count++;
		}
		else if (!fc_isCommit && fc_pending->is_create)
		{
			/*
			 * 创建对象的事务已回滚。丢弃与该对象相关的统计信息。
			 */
			if (!pgstat_drop_entry(fc_it->kind, fc_it->dboid, fc_it->objoid))
				fc_not_freed_count++;
		}

		dlist_delete(&fc_pending->node);
		fc_xact_state->pending_drops_count--;
		pfree(fc_pending);
	}

	if (fc_not_freed_count > 0)
		pgstat_request_entry_refs_gc();
}

/*
 * 从 access/transam/xact.c 调用，在子事务提交/回滚时。
 */
void AtEOSubXact_PgStat(bool fc_isCommit, int fc_nestDepth)
{
	PgStat_SubXactStatus *fc_xact_state;

	/* 将子事务的事务统计合并到父事务中 */
	fc_xact_state = pgStatXactStack;
	if (fc_xact_state != NULL &&
		fc_xact_state->nest_level >= fc_nestDepth)
	{
		/* 立即从栈中解除 xact_state 的链接，以简化重用情况 */
		pgStatXactStack = fc_xact_state->prev;

		AtEOSubXact_PgStat_Relations(fc_xact_state, fc_isCommit, fc_nestDepth);
		fc_AtEOSubXact_PgStat_DroppedStats(fc_xact_state, fc_isCommit, fc_nestDepth);

		pfree(fc_xact_state);
	}
}

/*
 * 类似于 AtEOXact_PgStat_DroppedStats()，但针对子事务。
 */
static void fc_AtEOSubXact_PgStat_DroppedStats(PgStat_SubXactStatus *fc_xact_state,
								bool fc_isCommit, int fc_nestDepth)
{
	PgStat_SubXactStatus *fc_parent_xact_state;
	dlist_mutable_iter fc_iter;
	int			fc_not_freed_count = 0;

	if (fc_xact_state->pending_drops_count == 0)
		return;

	fc_parent_xact_state = pgstat_get_xact_stack_level(fc_nestDepth - 1);

	dlist_foreach_modify(fc_iter, &fc_xact_state->pending_drops)
	{
		PgStat_PendingDroppedStatsItem *fc_pending =
		dlist_container(PgStat_PendingDroppedStatsItem, node, fc_iter.cur);
		xl_xact_stats_item *fc_it = &fc_pending->item;

		dlist_delete(&fc_pending->node);
		fc_xact_state->pending_drops_count--;

		if (!fc_isCommit && fc_pending->is_create)
		{
			/*
			 * 创建新的统计对象的子事务已回滚。丢弃统计对象。
			 */
			if (!pgstat_drop_entry(fc_it->kind, fc_it->dboid, fc_it->objoid))
				fc_not_freed_count++;
			pfree(fc_pending);
		}
		else if (fc_isCommit)
		{
			/*
			 * 丢弃统计对象的子事务已提交。还不能移除统计对象，周围的事务可能仍会回滚。将其传递给父事务。
			 */
			dlist_push_tail(&fc_parent_xact_state->pending_drops, &fc_pending->node);
			fc_parent_xact_state->pending_drops_count++;
		}
		else
		{
			pfree(fc_pending);
		}
	}

	Assert(fc_xact_state->pending_drops_count == 0);
	if (fc_not_freed_count > 0)
		pgstat_request_entry_refs_gc();
}

/*
 * 在 2PC 事务准备时保存事务统计状态。
 */
void AtPrepare_PgStat(void)
{
	PgStat_SubXactStatus *fc_xact_state;

	fc_xact_state = pgStatXactStack;
	if (fc_xact_state != NULL)
	{
		Assert(fc_xact_state->nest_level == 1);
		Assert(fc_xact_state->prev == NULL);

		AtPrepare_PgStat_Relations(fc_xact_state);
	}
}

/*
 * 在成功的 PREPARE 之后进行清理。
 *
 * 注意：在 PREPARE 期间不调用 AtEOXact_PgStat。
 */
void PostPrepare_PgStat(void)
{
	PgStat_SubXactStatus *fc_xact_state;

	/*
	 * 我们不打算释放任何事务状态，因为它们都在 TopTransactionContext 中，并且反正会消失。
	 */
	fc_xact_state = pgStatXactStack;
	if (fc_xact_state != NULL)
	{
		Assert(fc_xact_state->nest_level == 1);
		Assert(fc_xact_state->prev == NULL);

		PostPrepare_PgStat_Relations(fc_xact_state);
	}
	pgStatXactStack = NULL;

	/* 确保任何统计快照被丢弃 */
	pgstat_clear_snapshot();
}

/*
 * 确保给定的 nest_level 的（子）事务栈条目存在，如有必要则添加它。
 */
PgStat_SubXactStatus *
pgstat_get_xact_stack_level(int fc_nest_level)
{
	PgStat_SubXactStatus *fc_xact_state;

	fc_xact_state = pgStatXactStack;
	if (fc_xact_state == NULL || fc_xact_state->nest_level != fc_nest_level)
	{
		fc_xact_state = (PgStat_SubXactStatus *)
			MemoryContextAlloc(TopTransactionContext,
							   sizeof(PgStat_SubXactStatus));
		dlist_init(&fc_xact_state->pending_drops);
		fc_xact_state->pending_drops_count = 0;
		fc_xact_state->nest_level = fc_nest_level;
		fc_xact_state->prev = pgStatXactStack;
		fc_xact_state->first = NULL;
		pgStatXactStack = fc_xact_state;
	}
	return fc_xact_state;
}

/*
 * 获取需要在提交/回滚时丢弃的统计项。
 *
 * 在提交时，返回在事务中已丢弃对象的统计信息。在回滚时，返回新创建对象的统计信息。
 *
 * 在构建各自的 WAL 记录时，COMMIT/ABORT 和 2PC PREPARE 处理使用此信息，以确保在崩溃/备份时丢弃统计信息。
 *
 * 项目的列表在 CurrentMemoryContext 中分配，必须由调用者释放（直接或通过内存上下文重置）。
 */
int pgstat_get_transactional_drops(bool fc_isCommit, xl_xact_stats_item **fc_items)
{
	PgStat_SubXactStatus *fc_xact_state = pgStatXactStack;
	int			fc_nitems = 0;
	dlist_iter	fc_iter;

	if (fc_xact_state == NULL)
		return 0;

	/*
	 * 我们预计会被调用用于子事务回滚（会记录 WAL），但不会用于子事务提交（不会记录）。
	 */
	Assert(!fc_isCommit || fc_xact_state->nest_level == 1);
	Assert(!fc_isCommit || fc_xact_state->prev == NULL);

	*fc_items = palloc(fc_xact_state->pending_drops_count
					* sizeof(xl_xact_stats_item));

	dlist_foreach(fc_iter, &fc_xact_state->pending_drops)
	{
		PgStat_PendingDroppedStatsItem *fc_pending =
		dlist_container(PgStat_PendingDroppedStatsItem, node, fc_iter.cur);

		if (fc_isCommit && fc_pending->is_create)
			continue;
		if (!fc_isCommit && !fc_pending->is_create)
			continue;

		Assert(fc_nitems < fc_xact_state->pending_drops_count);
		(*fc_items)[fc_nitems++] = fc_pending->item;
	}

	return fc_nitems;
}

/*
 * 在提交后执行计划中的丢弃。 从 xact_redo_commit() / xact_redo_abort() 在恢复期间调用，以及从 FinishPreparedTransaction() 在正常 2PC COMMIT/ABORT PREPARED 处理中调用。
 */
void pgstat_execute_transactional_drops(int fc_ndrops, struct xl_xact_stats_item *fc_items, bool fc_is_redo)
{
	int			fc_not_freed_count = 0;

	if (fc_ndrops == 0)
		return;

	for (int fc_i = 0; fc_i < fc_ndrops; fc_i++)
	{
		xl_xact_stats_item *fc_it = &fc_items[fc_i];

		if (!pgstat_drop_entry(fc_it->kind, fc_it->dboid, fc_it->objoid))
			fc_not_freed_count++;
	}

	if (fc_not_freed_count > 0)
		pgstat_request_entry_refs_gc();
}

static void fc_create_drop_transactional_internal(PgStat_Kind fc_kind, Oid fc_dboid, Oid fc_objoid, bool fc_is_create)
{
	int			fc_nest_level = GetCurrentTransactionNestLevel();
	PgStat_SubXactStatus *fc_xact_state;
	PgStat_PendingDroppedStatsItem *fc_drop = (PgStat_PendingDroppedStatsItem *)
	MemoryContextAlloc(TopTransactionContext, sizeof(PgStat_PendingDroppedStatsItem));

	fc_xact_state = pgstat_get_xact_stack_level(fc_nest_level);

	fc_drop->is_create = fc_is_create;
	fc_drop->item.kind = fc_kind;
	fc_drop->item.dboid = fc_dboid;
	fc_drop->item.objoid = fc_objoid;

	dlist_push_tail(&fc_xact_state->pending_drops, &fc_drop->node);
	fc_xact_state->pending_drops_count++;
}

/*
 * 以事务方式为新创建的数据库对象创建统计条目。
 *
 * 也就是说，如果当前（子）事务回滚，统计条目也会被丢弃。
 */
void pgstat_create_transactional(PgStat_Kind fc_kind, Oid fc_dboid, Oid fc_objoid)
{
	if (pgstat_get_entry_ref(fc_kind, fc_dboid, fc_objoid, false, NULL))
	{
		ereport(WARNING,
				errmsg("resetting existing statistics for kind %s, db=%u, oid=%u",
					   (pgstat_get_kind_info(fc_kind))->name, fc_dboid, fc_objoid));

		pgstat_reset(fc_kind, fc_dboid, fc_objoid);
	}

	fc_create_drop_transactional_internal(fc_kind, fc_dboid, fc_objoid, /* 创建 */ true);
}

/*
 * 以事务方式为刚刚丢弃的数据库对象丢弃统计条目。
 *
 * 也就是说，如果当前（子）事务回滚，统计条目将保持有效。
 */
void pgstat_drop_transactional(PgStat_Kind fc_kind, Oid fc_dboid, Oid fc_objoid)
{
	fc_create_drop_transactional_internal(fc_kind, fc_dboid, fc_objoid, /* 创建 */ false);
}
